/**
 * 谁是卧底
 * 游戏介绍：系统发牌器给出两个类似的词语，分的少数牌的为卧底，通过轮流发言
 * 每个人用一句话对自己的词语进行描述，最后投票指认卧底出局
 *
 * 分牌规则：3-5人局一张卧底，6-8人局有两张卧底，9-12人局有三张卧底，12人以上局四张卧底
 *
 * 游戏规则：玩家在描述自己的词语时，不准提及词语的几个字，描述中不准出现包含该词语的字，否则直接淘汰出局
 *
 * 胜利规则：
 *  好人：卧底全部被投票淘汰，则好人胜利
 *
 *  卧底：好人数量减至比卧底数量多1，则卧底胜利
 */
const { default: words } = require('./word')

const introduce = `
谁是卧底
  游戏介绍：系统发牌器给出两个类似的词语，分的少数牌的为卧底，通过轮流发言每个人用一句话对自己的词语进行描述，最后投票指认卧底出局
  
  分牌规则：4-5人局一张卧底，6-8人局有两张卧底，9-12人局有三张卧底，12人以上局四张卧底
  
  游戏规则：玩家在描述自己的词语时，不准提及词语的几个字，否则直接淘汰出局
  
  胜利规则：
    好人：卧底全部被投票淘汰，则好人胜利
    卧底：好人数量减至比卧底数量多1，则卧底胜利
   
    如需加入，请发送“加入游戏”，游戏开始后，词语将私发至您的个人微信
    如需开始，请发送“开始游戏”，不足四人，无法开始。
    如需投票，请发送“投票@玩家”，弃票，请发送“弃票”。
    如需查询描述或投票状态，请发送“查状态”。
    如需结束，请发送“结束游戏”，游戏将直接结束。
`
const sendTip = '词语已发送，请注意查收，下面进行第一轮描述。'

export default class Undercover {
  constructor () {
    this.players = [] // { userName, nickName, isUndercover 是否是卧底， isDisuse 是否淘汰， word  拿到的词， rounds 玩家轮次, voteRound 投票轮次， votedNum 被投票数量, deadLock 平票人员 }
    this.count = 0 // 玩家数量
    this.status = 0 // 0-未开始，1-开始, 2-描述结束
    this.rounds = 0 // 轮次
    this.words = this.getRandomWord()
    this.undercoverWordIndex = Math.floor(Math.random() * 2) // 卧底词下标
    this.undercoverIndexList = [] // 卧底下标
    this.undercoverDes = '卧底玩家为'
    this.speechIndex = 0 // 发言索引
    this.deadLockCount = 0 // 平票次数
    this.deadLockIndex = 0 // 平票索引
  }
  init ({ bot, msg }) {
    bot.sendMsg(introduce, msg.FromUserName)
  }

  run ({ bot, msg, content, currentContact, botNickName }) {
    const currentUser = this.players.find(
      item => item.userName === currentContact.UserName && !item.isDisuse
    )
    // 如果不是玩家或淘汰玩家说话，过滤
    if (this.status > 0 && !currentUser) return

    if (content === '结束游戏') {
      bot.sendMsg('游戏结束', msg.FromUserName)
      bot.emit('exitGame', msg.FromUserName)
      return
    }

    // 投票阶段
    if (this.status === 2) {
      this.vote({ bot, msg, content, currentUser })
      return
    }

    if (content === '加入游戏' && this.status === 0) {
      this.join({ bot, msg, currentUser, currentContact, botNickName })
      return
    }

    if (content === '开始游戏' && this.rounds === 0) {
      this.start(bot, msg)
      return
    }
    // 描述阶段
    if (this.status === 1) {
      this.description({ bot, msg, content, currentUser })
    }
  }
  join ({ bot, msg, currentUser, currentContact, botNickName }) {
    if (currentUser) {
      bot.sendMsg('您已加入游戏，请耐心等待游戏开始。', msg.FromUserName)
      return
    }
    const contactsUserName = Object.keys(bot.contacts).map(key => key)
    if (!contactsUserName.includes(currentContact.UserName)) {
      // 非好友，目前无法拉起好友申请
      // bot.addFriend(player.userName, word)
      bot.sendMsg(
        `您还不是${botNickName}的好友，请先添加好友再参与游戏。`,
        msg.FromUserName
      )
      return
    }
    this.count++
    if (this.count > 12) {
      bot.sendMsg('人数最多12人！', msg.FromUserName)
      return
    }
    const nickName = currentContact.NickName
    this.players.push({
      userName: currentContact.UserName,
      nickName,
      isUndercover: false,
      isDisuse: false,
      word: '',
      rounds: 0,
      voteRound: 0,
      votedNum: 0,
      deadLock: false
    })

    bot.sendMsg(
      `玩家${nickName}加入成功，当前人数为${this.count}`,
      msg.FromUserName
    )
  }
  start (bot, msg) {
    if (this.count < 4) {
      bot.sendMsg('人数不足4人，无法开始', msg.FromUserName)
      return
    }
    this.rounds = 1
    this.status = 1
    this.getUndercoverPlayer()
    bot.sendMsg('正在私发词语...', msg.FromUserName)
    const normalIndex = this.undercoverWordIndex === 0 ? 1 : 0
    this.players.forEach(player => {
      const word = player.isUndercover
        ? this.words[this.undercoverWordIndex]
        : this.words[normalIndex]
      player.word = word
      // 是好友
      bot.sendMsg(`您的词语为 ${word}`, player.userName)
    })
    this.shuffleArray(this.players)
    bot.sendMsg(`${sendTip}请@${this.players[this.speechIndex].nickName} 进行描述。`, msg.FromUserName)
  }
  description ({ bot, msg, content, currentUser }) {
    if (content === '查状态') {
      const unDesPlayers = this.players.filter(
        item => (item.rounds !== this.rounds && !item.isDisuse)
      )
      bot.sendMsg(
        `${unDesPlayers
          .map(item => item.nickName)
          .join('、')}还未描述，请耐心等待`,
        msg.FromUserName
      )
      return
    }

    // 按发言索引发言
    if (currentUser.nickName === this.players[this.speechIndex].nickName) {
      this.speechIndex++
      while (this.speechIndex < this.count - 1 && this.players[this.speechIndex] && this.players[this.speechIndex].isDisuse) {
        this.speechIndex++
      }
      // 判断玩家说话与当前轮次是否匹配
      if (currentUser.rounds < this.rounds) {
        currentUser.rounds++
      }
      // 如果玩家说的描述包含关键词，直接淘汰
      const wordReg = new RegExp(`(${currentUser.word.split('').join('|')})`)
      if (wordReg.test(content)) {
        currentUser.isDisuse = true
        const res = this.isEnd(
          bot,
          msg,
          `您说的词语包含关键词，您被淘汰，${currentUser.nickName}${
            currentUser.isUndercover ? '是' : '不是'
          }卧底。`
        )
        if (res) return
      }

      // 如果所有玩家的轮次都与当前轮次匹配则进入下一轮，淘汰玩家不以轮次确认
      if (
        this.players.every(item => item.rounds === this.rounds || item.isDisuse)
      ) {
        bot.sendMsg(
          `第${this.rounds}轮描述结束，请按照投票格式进行投票`,
          msg.FromUserName
        )
        this.speechIndex = 0
        this.status = 2
      } else {
        bot.sendMsg(
          `请@${this.players[this.speechIndex].nickName} 进行描述。`,
          msg.FromUserName
        )
      }
    }
  }
  vote ({ bot, msg, content, currentUser }) {
    if (content === '查状态') {
      const unVotePlayers = this.players.filter(
        item => (item.voteRound !== this.rounds && !item.isDisuse)
      )
      bot.sendMsg(
        `${unVotePlayers
          .map(item => item.nickName)
          .join('、')}还未投票，请耐心等待`,
        msg.FromUserName
      )
      return
    }
    if (content.startsWith('投票') || content === '弃票') {
      const nickName = content.split('@')[1]
      const votedUser = this.players.find(item => item.nickName === nickName)
      if (votedUser && votedUser.isDisuse) {
        bot.sendMsg('该玩家已出局，不可再投。请重新投票', msg.FromUserName)
        return
      }
      if (currentUser.voteRound === this.rounds) {
        if (this.deadLockIndex > 0) {
          // 说明平票，只能投pk台玩家
          const deadLockPlayers = this.players.slice(0, this.deadLockIndex + 1)
          const deadLockPlayer = deadLockPlayers.find(item => item.nickName === votedUser.nickName)
          if (!deadLockPlayer) {
            bot.sendMsg('该玩家非平票玩家，不可投。请重新投票', msg.FromUserName)
          } else {
            bot.sendMsg('您为平票玩家，不可投。请等待投票结果', msg.FromUserName)
          }
          return
        }
        bot.sendMsg('您本轮已投票或投票结束，不可再投', msg.FromUserName)
        return
      }

      if (votedUser) {
        currentUser.voteRound++
        votedUser.votedNum++
      } else if (content === '弃票') {
        currentUser.voteRound++
      } else {
        bot.sendMsg('投票对象不存在或格式错误', msg.FromUserName)
        return
      }
    }

    // 票数过半直接判断
    const existLen = this.players.filter(item => !item.isDisuse).length
    const isHalf = this.players.some(
      item => item.votedNum > Math.floor(existLen / 2)
    )
    if (isHalf) {
      this.players.forEach(item => {
        item.voteRound = this.rounds
      })
    }
    // 所有人投票轮次与本轮次相同或者玩家出局
    const isAllVote = this.players.every(
      item => (item.voteRound === this.rounds || item.isDisuse)
    )
    if (isAllVote) {
      this.players.sort((a, b) => b.votedNum - a.votedNum)
      const outUser = this.players[0]
      let deadLockIndex
      this.players.forEach((item, index) => {
        if (item.votedNum === outUser.votedNum) {
          deadLockIndex = index
        }
      })
      this.deadLockIndex = deadLockIndex || 0
      if (this.deadLockIndex > 0) {
        this.deadLockCount++
        // 如果出现平票加一轮
        this.status = 1
        this.speechIndex = 0
        this.players.forEach((item, index) => {
          if (index <= this.deadLockIndex) {
            // 如果平票，平票玩家描述轮次重置
            item.rounds--
          } else {
            // 其余玩家获得投票权
            item.voteRound--
          }
          item.votedNum = 0
        })
        if (this.deadLockCount === 2) {
          this.rounds++
          this.shuffleArray(this.players)
          this.deadLockCount = 0
          this.deadLockIndex = 0
          bot.sendMsg(
            `本轮投票再次平局，直接进入下一轮描述。请@${this.players[this.speechIndex].nickName} 进行描述。`,
            msg.FromUserName
          )
          return
        }
        bot.sendMsg(
          `本轮投票平局，平票人员需进行复述，请@${this.players[this.speechIndex].nickName} 进行描述。`,
          msg.FromUserName
        )
        return
      }
      let res
      this.shuffleArray(this.players)
      this.speechIndex = 0
      if (outUser.votedNum !== 0) {
        outUser.isDisuse = true
        while (this.speechIndex < this.count - 1 && this.players[this.speechIndex] && this.players[this.speechIndex].isDisuse) {
          this.speechIndex++
        }
        res = this.isEnd(
          bot,
          msg,
          `本轮投票结束，${outUser.nickName}被淘汰，${
            outUser.isUndercover ? '是' : '不是'
          }卧底。`,
        )
      } else {
        bot.sendMsg(
          `第${this.rounds}轮投票结束，无人淘汰，直接进入下一轮描述。请@${this.players[this.speechIndex].nickName} 进行描述。`,
          msg.FromUserName
        )
      }
      if (!res) {
        this.status = 1
        this.rounds++
        this.players.forEach(item => {
          item.votedNum = 0
        })
      }
    }
  }
  // 词库选词
  getRandomWord () {
    return words[Math.floor(Math.random() * words.length)]
  }

  // 随机选定卧底
  getUndercoverPlayer () {
    const underCount = this.getUndercoverCount()
    while (this.undercoverIndexList.length < underCount) {
      let undercoverIndex = Math.floor(Math.random() * this.count)
      while (this.undercoverIndexList.includes(undercoverIndex)) {
        undercoverIndex = Math.floor(Math.random() * this.count)
      }
      this.undercoverIndexList.push(undercoverIndex)
    }
    let undercoverDes = ''
    this.undercoverIndexList.forEach(index => {
      this.players[index].isUndercover = true
      undercoverDes += this.players[index].nickName + ','
    })
    this.undercoverDes += undercoverDes
  }
  // 获取卧底数量
  getUndercoverCount () {
    const len = this.players.length
    if (len <= 5) {
      return 1
    }
    if (len <= 8) {
      return 2
    }
    if (len <= 12) {
      return 3
    }
    return 4
  }

  // 判断是否游戏结束
  isGameOver () {
    // 好人数量减至比卧底数量多1
    const aliveNormalCount = this.players.filter(
      item => !item.isDisuse && !item.isUndercover
    ).length
    const undercoverCount = this.players.filter(
      item => item.isUndercover && !item.isDisuse
    ).length
    const totalCount = aliveNormalCount + undercoverCount
    if ((totalCount === 3 && aliveNormalCount - 1 === undercoverCount) || (totalCount > 3 && aliveNormalCount === undercoverCount)) {
      return 2
    }
    if (undercoverCount === 0) {
      return 1
    }
    return 0
  }

  // 判断游戏是否结束
  isEnd (bot, msg, replyContent = '') {
    // 全部投票完毕，判断游戏是否结束，数据更新
    const gameOverFlag = this.isGameOver()
    if (gameOverFlag === 1) {
      bot.sendMsg(
        `${replyContent}游戏结束，好人获胜!` + this.undercoverDes.slice(0, -1),
        msg.FromUserName
      )
      bot.emit('exitGame', msg.FromUserName)
      return true
    }
    if (gameOverFlag === 2) {
      bot.sendMsg(
        `${replyContent}游戏结束，卧底获胜!` + this.undercoverDes.slice(0, -1),
        msg.FromUserName
      )
      bot.emit('exitGame', msg.FromUserName)
      return true
    }
    if (this.speechIndex < this.count) {
      replyContent += `请@${this.players[this.speechIndex].nickName} 进行描述。`
    }
    bot.sendMsg(
      `${replyContent}`,
      msg.FromUserName
    )
    return false
  }
  // 打乱数组顺序
  shuffleArray (arr) {
    for (let i = arr.length - 1; i > 0; i--) {
      const j = Math.floor(Math.random() * (i + 1));
      [arr[i], arr[j]] = [arr[j], arr[i]]
    }
  }
}
